﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Windows.Controls.Primitives;
using System.Collections.Specialized;

namespace for7raid.Control
{

    // Achieve AutoComplete TextBox or ComboBox
    public class AutoCompleteTextBox : TextBox
    {
        private Popup popup;
        private ListBox list;

        /// <summary>
        /// Initializes a new instance of the <see cref="AutoCompleteTextBox"/> class.
        /// </summary>
        public AutoCompleteTextBox()
        {
            //load and apply style to the ComboBox.
            ResourceDictionary rd = new ResourceDictionary();
            rd.Source = new Uri("/" + this.GetType().Assembly.GetName().Name +
                ";component/UserControls/AutoCompleteComboBoxStyle.xaml",
                 UriKind.Relative);
            this.Resources = rd;
            //disable default Text Search Function

        }

        /// <summary>
        ///  override OnApplyTemplate method 
        /// get TextBox control out of Combobox control, and hook up TextChanged event.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            //get the textbox control in the ComboBox control
            popup = this.Template.FindName("Popup", this) as Popup;
            list = this.Template.FindName("list", this) as ListBox;

            //disable Autoword selection of the TextBox
            AutoWordSelection = false;
            //handle TextChanged event to dynamically add Combobox items.
            this.TextChanged += new TextChangedEventHandler(textBox_TextChanged);
            this.PreviewKeyUp += new KeyEventHandler(AutoCompleteTextBox_PreviewKeyUp);
            this.MouseDoubleClick += new MouseButtonEventHandler(AutoCompleteTextBox_MouseDoubleClick);
            this.LostFocus += new RoutedEventHandler(AutoCompleteTextBox_LostFocus);

            list.KeyUp += new KeyEventHandler(list_KeyUp);
            list.MouseUp += new MouseButtonEventHandler(list_MouseUp);
            list.MouseLeave += new MouseEventHandler(list_MouseLeave);

        }

        void list_MouseLeave(object sender, MouseEventArgs e)
        {
            popup.IsOpen = false;
        }

        void AutoCompleteTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
              //popup.IsOpen = list.IsFocused; 
        }

        void list_MouseUp(object sender, MouseButtonEventArgs e)
        {
            this.Text = list.SelectedItem as string;
            popup.IsOpen = false; 
        }

        void AutoCompleteTextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            Items.Clear();
            foreach (var item in AutoSuggestionList)
                Items.Add(item);
            popup.IsOpen = Items.Count> 0;
        }

        void list_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                this.Text = list.SelectedItem as string;
                popup.IsOpen = false; 
            }
        }

        void AutoCompleteTextBox_PreviewKeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Down && this.IsFocused && !list.IsFocused)
            {
                list.Focus();
                list.SelectedIndex = 0;
                e.Handled = true;
            }
            if (e.Key == Key.Escape)
            {
                popup.IsOpen = false;
                this.Focus();
            }

        }

       
        void textBox_PreviewKeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Back)
            {
                TextBox textBox = sender as TextBox;
                textBox.AutoWordSelection = false;
                textBox.Focus();
                textBox.SelectionStart = textBox.Text.Length;
            }
        }


        public StringCollection AutoSuggestionList
        {
            get { return (StringCollection)GetValue(AutoSuggestionListProperty); }
            set { SetValue(AutoSuggestionListProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AutoSuggestionList.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AutoSuggestionListProperty =
            DependencyProperty.Register("AutoSuggestionList", typeof(StringCollection), typeof(AutoCompleteTextBox), new UIPropertyMetadata(null));



        public ObservableCollection<string> Items
        {
            get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
            set { SetValue(ItemsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Items.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemsProperty =
            DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(AutoCompleteTextBox), new UIPropertyMetadata(new ObservableCollection<string>()));



        /// <summary>
        /// main logic to generate auto suggestion list.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Controls.TextChangedEventArgs"/> 
        /// instance containing the event data.</param>
        void textBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            TextBox textBox = sender as TextBox;
            textBox.AutoWordSelection = false;
            this.Items.Clear();
            // if the word in the textbox is selected, then don't change item collection
            if ((textBox.SelectionStart != 0 || textBox.Text.Length == 0))
            {
                
                //add new filtered items according the current TextBox input
                if (!string.IsNullOrEmpty(textBox.Text))
                {
                    foreach (string s in this.AutoSuggestionList)
                    {
                        if (s.StartsWith(textBox.Text, StringComparison.InvariantCultureIgnoreCase))
                        {

                            //string unboldpart = s.Substring(textBox.Text.Length);
                            //string boldpart = s.Substring(0, textBox.Text.Length);
                            ////construct AutoCompleteEntry and add to the ComboBox
                            //AutoCompleteEntry entry = new AutoCompleteEntry(s, boldpart, unboldpart);
                            //this.Items.Add(entry);
                            this.Items.Add(s);

                        }
                    }
                }
            }
            // open or close dropdown of the ComboBox according to whether there are items in the 
            // fitlered result.
            popup.IsOpen = Items.Count()> 0;

            //avoid auto selection
            //textBox.Focus();
            //textBox.SelectionStart = textBox.Text.Length;

        }
    }

    //class derived from ComboBoxItem.
    /// <summary>
    /// Extended ComboBox Item
    /// </summary>
    public class AutoCompleteEntry : ComboBoxItem
    {
        private TextBlock tb;

        //text of the item
        private string _text;

        /// <summary>
        /// Contrutor of AutoCompleteEntry class
        /// </summary>
        /// <param name="text">All the Text of the item </param>
        /// <param name="bold">The already entered part of the Text</param>
        /// <param name="unbold">The remained part of the Text</param>
        public AutoCompleteEntry(string text, string bold, string unbold)
        {
            _text = text;
            tb = new TextBlock();
            //highlight the current input Text
            tb.Inlines.Add(new Run
            {
                Text = bold,
                FontWeight = FontWeights.Bold,
                Foreground = new SolidColorBrush(Colors.RoyalBlue)
            });
            tb.Inlines.Add(new Run { Text = unbold });
            this.Content = tb;
        }

        /// <summary>
        /// Gets the text.
        /// </summary>
        public string Text
        {
            get { return _text; }
        }
    }
}
